//////////////////////////////////////////////////////////////////////
// FSM.cpp
//
// SHEN Fangyang
// me@shenfy.com
//
// Copyright (C) SHEN Fangyang, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	FSM and FSM state object
//
//////////////////////////////////////////////////////////////////////
#include "grflib_stdafx.h"
#include "../include/MsgRouter.h"
#include "../include/FSM.h"

using namespace grflib::engine::AI;


////////////////////////////////////////////////////////
// CFSM Implementation
CFSM::CFSM() {}

CFSM::~CFSM()
{
	m_pCurState.reset();
	m_stateMap.clear();
}

void CFSM::AddState(const std::string &id, boost::shared_ptr<CFSMState> &pState)
{
	m_stateMap[id] = pState;
}

void CFSM::RemoveState(const std::string &id)
{
	StateMap::iterator iter;
	if ((iter = m_stateMap.find(id)) != m_stateMap.end())
	{
		m_stateMap.erase(iter);
	}
}

void CFSM::SetStartState(const std::string &id)
{
	StateMap::iterator iter;
	if ((iter = m_stateMap.find(id)) != m_stateMap.end())
	{
		m_pCurState = iter->second;
	}	
}

void CFSM::AddState(char *szId, boost::shared_ptr<CFSMState> &pState)
{
	std::string id = szId;
	AddState(id, pState);
}

void CFSM::RemoveState(char *szId)
{
	std::string id = szId;
	RemoveState(id);
}

void CFSM::SetStartState(char *szId)
{
	std::string id = szId;
	SetStartState(id);
}

boost::shared_ptr<CFSMState> CFSM::GetCurrentState(void)
{
	return m_pCurState;
}

std::string CFSM::StateToId(CFSMState *pState)
{
	for (StateMap::iterator iter = m_stateMap.begin();
		 iter != m_stateMap.end(); 
		 ++iter)
	{
		if (iter->second.get() == pState)
			return iter->first;
	}
	return "";
}

bool CFSM::OnMsg(const CMsg &msg)
{
	std::string nextState;
	//call state's transition method
	if (m_pCurState->GetNextState(msg, nextState)) //state changed
	{
		//verify if output state is valid
		StateMap::iterator iter = m_stateMap.find(nextState);
		if (iter != m_stateMap.end())
		{
			m_pCurState->OnExit(msg);
			m_pCurState->Byproduct(msg);
			m_pCurState = iter->second;
			m_pCurState->OnEnter(msg);
			return true;
		}
	}
	return false;
}

bool CFSM::ForceCurrentState(const std::string &id)
{
	StateMap::iterator iter = m_stateMap.find(id);
	if (iter != m_stateMap.end())
	{
		m_pCurState = iter->second;
		return true;
	}
	return false;
}

////////////////////////////////////////////////////////
// CFSMState Implementation
CFSMState::CFSMState()
{}

CFSMState::~CFSMState()
{
	Clear();
}

void CFSMState::OnEnter(const CMsg &msg)
{
	if (!m_onEnterFunc.empty())
		m_onEnterFunc(msg);
}

void CFSMState::OnExit(const CMsg &msg)
{
	if (!m_onExitFunc.empty())
		m_onExitFunc(msg);
}

void CFSMState::Byproduct(const CMsg &msg)
{
	ByproductTable::iterator iter = m_byprodTable.find(msg.m_id);
	if (iter != m_byprodTable.end())
	{
		iter->second(msg);
	}
}

ByproductTable &CFSMState::GetByProdTable()
{
	return m_byprodTable;
}

bool CFSMState::GetNextState(const CMsg &msg, std::string &outputState)
{
	TransitionTable::iterator iter = m_transTable.find(msg.m_id);
	if (iter != m_transTable.end()) //transition path exists
	{
		outputState = iter->second;
		return true;
	}
	//else, ignore msg
	return false;
}

void CFSMState::AddTransition(std::string msgId, std::string destState)
{
	m_transTable[msgId] = destState;
}

void CFSMState::AddTransition(std::string msgId, std::string destState,
							  boost::function<void (const CMsg &)> &byprodFunc)
{
	m_transTable[msgId] = destState;
	m_byprodTable[msgId] = byprodFunc;
}

void CFSMState::Clear()
{
	m_transTable.clear();
	m_byprodTable.clear();
	m_onEnterFunc.clear();
	m_onExitFunc.clear();
}